/**
 * 
 */
package com.goktech.commons.excel.utils.abs;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.goktech.commons.excel.Event;
import com.goktech.commons.excel.Excel;
import com.goktech.commons.excel.ExcelArrayModel;
import com.goktech.commons.excel.ExcelConfig;
import com.goktech.commons.excel.ExcelException;
import com.goktech.commons.excel.ExcelModel;
import com.goktech.commons.excel.ExcelStyle;
import com.goktech.commons.excel.Font;
import com.goktech.commons.excel.Rows;
import com.goktech.commons.excel.Style;
import com.goktech.commons.excel.SummaryInformation;
import com.goktech.commons.excel.Title;
import com.goktech.commons.excel.Titles;
import com.goktech.commons.excel.utils.ValueSelector;
import com.goktech.commons.excel.utils.event.StatsticsEvent;

/**
 * @author zhongmh
 *
 */
public abstract class AbstractExcelWriter<T> extends AbstractExcel<T> {

	protected Logger logger = LoggerFactory.getLogger(this.getClass());

	protected Map<String, Object> data;

	protected Map<String, Excel> excelMap;

	protected List<String> paramsList;

	protected Map<String, Title> titleMap;

	protected Font font;

	protected Style style;

	protected SummaryInformation summaryInformation;

	protected List<Rows> rowsList;

	protected ExcelModel model = new ExcelArrayModel(10, 10);

	protected ExcelConfig excelConfig;

	protected Sheet sheet;

	protected int rowIndex = 0;// 行索引

	protected ExcelStyle excelStyle;

	private StatsticsEvent event;

	private Map<String, Event> statisticsMap;

	protected String titleFielName;

	public AbstractExcelWriter(Class<?> clazz) {
		super(clazz);
		this.font = this.getFont(clazz);
		this.style = this.getStyle(clazz);
		this.rowsList = new ArrayList<>(1);
		this.rowsList.add(this.getRows(clazz));
		this.titleMap = new HashMap<String, Title>();
		this.paramsList = new ArrayList<String>(10);
	}

	public AbstractExcelWriter(Class<?> clazz, ExcelConfig excelConfig) {
		super(clazz);
		this.font = this.getFont(clazz);
		this.style = this.getStyle(clazz);
		this.rowsList = new ArrayList<>(1);
		this.rowsList.add(this.getRows(clazz));
		this.excelConfig = excelConfig;
		this.paramsList = new ArrayList<String>(10);
	}

	private Font getFont(Class<?> clazz) {
		Font font = clazz.getAnnotation(Font.class);
		return font;
	}

	private Style getStyle(Class<?> clazz) {
		Style style = clazz.getAnnotation(Style.class);
		return style;
	}

	public abstract void write(OutputStream outputStream) throws IOException;

	protected void getProperty() {
		// 每次获取变量值的时候都初始化
		this.excelMap = new HashMap<>();
		this.paramsList = new ArrayList<>();
		this.statisticsMap = new HashMap<>();
		getProperty(this.clazz);
		if (logger.isDebugEnabled()) {
			logger.debug("map containder init success");
		}
	}

	private void getProperty(Class<?> clazz) {
		Field[] fields = clazz.getDeclaredFields();
		if (fields.length <= 0) {
			return;
		}
		for (Field field : fields) {
			Excel obj = field.getAnnotation(Excel.class);
			if (obj != null) {
				this.excelMap.put(field.getName(), obj);
				this.paramsList.add(field.getName());
			}
			Event statistics = field.getAnnotation(Event.class);
			if (statistics != null) {
				this.statisticsMap.put(field.getName(), statistics);
			}
			Titles titles = field.getAnnotation(Titles.class);
			if (titles != null) {
				this.titleFielName = field.getName();
			}
		}
		getProperty(clazz.getSuperclass());
	}

	protected Row createRow(int index) {
		Row row = this.sheet.createRow(rowIndex);
		this.rowIndex++;
		return row;
	}

	protected Cell createCell(Row row, int index) {
		Cell cell = row.createCell(index);
		cell.setCellStyle(this.excelStyle.getStyle(""));
		return cell;
	}

	protected abstract void setSummaryInformatioin();

	/**
	 * 获取是否自适应的报个样式
	 * 
	 * @return
	 */
	public boolean getAutoSizeColumn() {
		if (summaryInformation == null)
			return false;
		return summaryInformation.autoSizeColumn();
	}

	public Rows getRows(Class<?> clazz) {
		Rows rows = clazz.getAnnotation(Rows.class);
		return rows;
	}

	public Rows getLastRowsList() {
		Rows rows = this.clazz.getAnnotation(Rows.class);
		if (rows != null && (rowsList.size() > 0 && rows.equals(rowsList.get(0)))) {
			this.rowsList.add(rows);
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Rows not found");
			}

		}
		return this.rowsList.size() > 0 ? rowsList.get(rowsList.size() - 1) : null;
	}

	public T addStatsticsEvent(StatsticsEvent event) {
		this.event = event;
		return this.getSelf();
	}

	/**
	 * 初始化行
	 */
	public abstract T initRows();

	/**
	 * 初始化表头
	 */
	public abstract T initTitle();

	/**
	 * 在表中添加数据
	 * 
	 * @param collection
	 * @return
	 */
	public T forEach(Collection<?> collection) {
		if (this.paramsList.size() == 0) {
			getProperty();
		}
		if (logger.isDebugEnabled()) {
			logger.debug("当前导出数据的总数：{}", collection == null ? 0 : collection.size());
		}
		for (Iterator<?> it = collection.iterator(); it.hasNext();) {
			Row row = this.createRow(rowIndex);
			Object obj = it.next();
			for (int i = 0; i < paramsList.size(); i++) {
				Cell cell = createCell(row, i);
				if (obj == null) {
					cell.setCellValue(this.excelMap.get(this.paramsList.get(i)).defaultValue());
				} else {
					if (this.excelMap.get(this.paramsList.get(i)) != null) {
						Object value = propertyUtils.getCacheProperty(obj, paramsList.get(i));
						ValueSelector.getValue(value, cell, this.excelMap.get(this.paramsList.get(i)));
					} else if (this.titleFielName != null) {
						Object value = this.getValueOfMap(obj, this.paramsList.get(i), titleFielName);
						ValueSelector.setValue(value, cell);
					}
				}
				if (this.statisticsMap.get(this.paramsList.get(i)) != null) {
					if (event != null) {
						event.listener(cell, this.paramsList.get(i));// 触发事件
					}
				}
			}
		}
		return this.getSelf();
	}

	/**
	 * 批量增加表头
	 * 
	 * @param titles
	 * @return
	 */
	public T addTitle(List<Title> titles) {
		if (this.paramsList.size() == 0) {
			getProperty();
		}
		return addTitle(this.getTitleArray() == null ? 0 : this.getTitleArray().size(), titles);
	}

	public T addTitle(int index, List<Title> titles) {
		for (Title title : titles) {
			addTitle(index++, title);
		}
		return this.getSelf();
	}

	/**
	 * 增加表头
	 * 
	 * @param title
	 * @return
	 */
	public T addTitle(Title title) {
		if (this.paramsList.size() == 0) {
			getProperty();
		}
		addTitle(this.getTitleArray() == null ? 0 : this.getTitleArray().size(), title);
		return this.getSelf();
	}

	/**
	 * 增加表头
	 * 
	 * @param index
	 *            增加位置的索引
	 * @param title
	 * @return
	 */
	public T addTitle(int index, Title title) {
		if (this.paramsList.size() == 0) {
			getProperty();
		}
		this.titleMap.put(title.getFieldName(), title);
		// 维护表头索引Map 全部+1
		this.paramsList.add(index, title.getFieldName());
		return this.getSelf();
	}

	/**
	 * Map的名字
	 * 
	 * @param key
	 * @param propertyName
	 * @return
	 */
	private Object getValueOfMap(Object object, String key, String propertyName) {
		// 通过反射获取hasnmap
		Object map = this.propertyUtils.getCacheProperty(object, propertyName);
		if (map instanceof Map) {
			return ((Map<String, Object>) map).get(key);
		}
		throw new ExcelException("数据类型错误,propertyName：" + propertyName);
	}

	public T putData(Map<String, Object> data) {
		this.data = data;
		return this.getSelf();
	}

	/**
	 * <p>
	 * 获取表头数组对象
	 * </p>
	 * 
	 * @return
	 */
	public List<String> getTitleArray() {
		return this.paramsList;
	}

	public T setClass(Class<?> clazz) {
		super.setClass(clazz);
		this.font = this.getFont(clazz);
		this.style = this.getStyle(clazz);
		this.rowsList = new ArrayList<>(1);
		this.rowsList.add(this.getRows(clazz));
		this.paramsList = new ArrayList<String>(10);
		this.rowIndex = 0;
		return this.getSelf();
	}

	public int getRowIndex() {
		return this.rowIndex;
	}

	public abstract T createSheet(String name);

	public abstract T initStyle();

	public abstract Workbook workbook();
}
